package cn.rxls.community.service.impl;

import cn.rxls.community.entity.billEntity.BillHouse;
import cn.rxls.community.entity.billEntity.BillLife;
import cn.rxls.community.contant.BillDateConstant;
import cn.rxls.community.dto.bill.AddBillDTO;
import cn.rxls.community.dto.bill.PageBillCategoryDTO;
import cn.rxls.community.entity.Bill;
import cn.rxls.community.entity.Contractual;

import cn.rxls.community.entity.ResourceUsage;
import cn.rxls.community.enums.*;
import cn.rxls.community.exception.BusinessException;
import cn.rxls.community.mapper.*;

import cn.rxls.community.service.BillService;
import cn.rxls.community.strategy.bill.BillStrategy;
import cn.rxls.community.strategy.bill.BillStrategyFactory;
import cn.rxls.community.util.HttpUtil;
import cn.rxls.community.vo.bill.BillVO;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.*;
import java.time.temporal.ChronoUnit;

import java.time.temporal.TemporalAdjusters;
import java.util.Date;
import java.util.List;
import java.util.Optional;


/**
 * 账单(Bill)表服务实现类
 */
@Service("billService")
@Transactional(rollbackFor = Exception.class)
public class BillServiceImpl extends ServiceImpl<BillMapper, Bill> implements BillService {

    private final BillMapper billMapper;

    private final UserHousesMapper userHousesMapper;

    private final ResourceUsageMapper resourceUsageMapper;

    private final ContractualMapper contractualMapper;

    BillServiceImpl(
            ContractualMapper contractualMapper,
            UserHousesMapper userHousesMapper,
            ResourceUsageMapper resourceUsageMapper,
            BillMapper billMapper
    ) {
        this.billMapper = billMapper;
        this.contractualMapper = contractualMapper;
        this.userHousesMapper = userHousesMapper;
        this.resourceUsageMapper = resourceUsageMapper;
    }

    /**
     * 分页查询账单根据分类
     *
     * @param dto 参数对象
     * @return 多条账单信息
     */
    @Override
    public List<BillVO> selectPageBillCategory(PageBillCategoryDTO dto) {
        //用户id
        String uid = String.valueOf(HttpUtil.getUserLoginInfo().get("uid"));

        //分页处理
        dto.setPage((dto.getPage() - 1) * dto.getSize());

        //获取账单分类id
        if (!BillTypeEnum.checkType(dto.getCategory())) {
            throw new BusinessException(ResultEnum.ERROR_TYPE_BILL);
        }

        List<BillVO> billVOS = billMapper.selectPageByCategory(dto.getPage(), dto.getSize(), Long.valueOf(uid), dto.getCategory());

        billVOS.forEach(s -> {
            BillStrategy strategy = BillStrategyFactory.getStrategy(dto.getCategory());
            Optional.ofNullable(strategy).orElseThrow(() -> new BusinessException(ResultEnum.ERROR_TYPE_BILL));
            Object parse = strategy.jsonString(s.getBillSpecifics());
            Optional.ofNullable(parse).orElseThrow(() -> new BusinessException(ResultEnum.ERROR_JSON_BILL));
            s.setBillContent(parse);
            s.setBillSpecifics(null);
        });

        return billVOS;
    }


    /**
     * 根据类型添加账单
     *
     * @param dto 参数对象
     * @return 是否成功
     */
    @Override
    public Boolean addBillByCategory(AddBillDTO dto) {
        BillStrategy strategy = BillStrategyFactory.getStrategy(dto.getBillCategory());
        Optional.ofNullable(strategy).orElseThrow(() -> new BusinessException(ResultEnum.ERROR_TYPE_BILL));
        return strategy.addBill(dto);
    }

    /**
     * 根据id查询账单
     *
     * @param id 账单id
     * @return 单条信息
     */
    @Override
    public BillVO selectByDetail(Long id) {
        //获取用户信息
        String uid = String.valueOf(HttpUtil.getUserLoginInfo().get("uid"));
        LambdaQueryWrapper<Bill> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Bill::getBillId, id).eq(Bill::getBillUser, Long.valueOf(uid));
        Bill bill = billMapper.selectOne(wrapper);
        Optional.ofNullable(bill).orElseThrow(() -> new BusinessException(ResultEnum.ERROR_BILL));
        BillVO billVO = new BillVO();
        BeanUtils.copyProperties(bill, billVO);
        Optional.ofNullable(bill.getBillSpecifics()).ifPresent(s -> {
            billVO.setBillContent(JSON.parseObject(billVO.getBillSpecifics()));
        });
        billVO.setBillSpecifics(null);
        return billVO;
    }


    /**
     * 每月房屋租金账单
     */
    @Scheduled(cron = "0 0 0 * * ?")
    public void monthlyHouseBills() {
        LocalDate today = LocalDate.now();
        //如果这天不是创建账单日期范围则直接结束
        if (today.getDayOfMonth() < BillDateConstant.HOUSE_SETTLEMENT || today.getDayOfMonth() > BillDateConstant.HOUSE_SETTLEMENT_END) {
            return;
        }

        // 获取当前日期
        LocalDate currentDate = LocalDate.now();

        //获取本月没有房屋账单的用户id列表
        List<Long> uidList = billMapper.selectByNoRecord(currentDate.getYear(), currentDate.getMonth().getValue(), null, BillTypeEnum.HOUSE.msg);

        //第几页
        int page = 0;
        //查询几个
        int size = 100;
        //获取所有的有效期内的合同
        LambdaQueryWrapper<Contractual> contractualWrapper = new LambdaQueryWrapper<>();
        contractualWrapper.eq(Contractual::getContractualState, ContractualStateEnum.VALID.code)
                .in(uidList.size() > 0, Contractual::getContractualUser, uidList);
        Page<Contractual> pageQuery = new Page<>(page, size);
        pageQuery.setSearchCount(false);
        do {
            page += 1;
            pageQuery.setCurrent(page);
            //每次查询100条用户记录
            Page<Contractual> pageList = contractualMapper.selectPage(pageQuery, contractualWrapper);
            if (pageList.getRecords().size() < 1 || uidList.size() < 1) {
                break;
            }

            //创建账单
            pageList.getRecords().forEach(s -> {
                Bill bill = new Bill();
                bill.setBillUser(s.getContractualUser());
                bill.setBillCategory(BillTypeEnum.HOUSE.msg);
                bill.setBillState(BillStateEnum.WARN_PAY.code);
                bill.setBillMsg(s.getHouseNumber());
                Long total_price = housingBillCalculations(s.getHouseRent(), s.getServiceFees(), s.getInternetFees());
                bill.setBillPrice(total_price);
                BillHouse billHouse = new BillHouse();
                billHouse.setRent(total_price);
                billHouse.setAddress(s.getHouseNumber());
                billHouse.setContractNumber(s.getContractualNumber());
                billHouse.setServiceFees(s.getServiceFees());
                billHouse.setInternetFees(s.getInternetFees());

                // 获取本月15日的时间，将LocalDate转换为Date
                LocalDate startlocalDate = currentDate.withDayOfMonth(BillDateConstant.HOUSE_SETTLEMENT);
                Date startDateTime = Date.from(startlocalDate.atStartOfDay(ZoneId.systemDefault()).toInstant());

                // 计算下一个月15日的日期
                LocalDate endLocalDate = currentDate.plusMonths(1).withDayOfMonth(BillDateConstant.HOUSE_SETTLEMENT);
                // 将LocalDate转换为Date
                Date endDateTime = Date.from(endLocalDate.atStartOfDay(ZoneId.systemDefault()).toInstant());

                //设置周期开始时间
                billHouse.setStartTime(startDateTime);
                //设置周期结束时间
                billHouse.setEndTime(endDateTime);


                // 计算合同签订时间与当前时间相差几个月
                // 将日期转换为YearMonth对象，以便比较月份
                // 将Date转换为Instant
                Instant instant = s.getStartTime().toInstant();
                // 将Instant转换为LocalDate，默认使用系统时区
                LocalDate localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate();
                YearMonth startYearMonth = YearMonth.from(localDate);
                YearMonth endYearMonth = YearMonth.from(startlocalDate);
                long monthsBetween = ChronoUnit.MONTHS.between(startYearMonth, endYearMonth);
                billHouse.setCycle((int) monthsBetween + 1);
                bill.setBillSpecifics(JSON.toJSONString(billHouse));
                if (billMapper.insert(bill) < 1) {
                    throw new BusinessException(ResultEnum.ERROR_ADD_BILL);
                }
            });
        } while (true);


    }

    /**
     * 每月水电账单
     */
    @Scheduled(cron = "0 0 0 * * ?")
    public void monthlyHydropowerBills() {
        LocalDate today = LocalDate.now();
        //如果这天不是创建账单日期范围则直接结束
        if (today.getDayOfMonth() < BillDateConstant.LIFE_SETTLEMENT || today.getDayOfMonth() > BillDateConstant.LIFE_SETTLEMENT_END) {
            return;
        }

        //获取本月1日
        LocalDate thisMonthMonday = LocalDate.now().withDayOfMonth(1);

        //获取本月没有水电账单的用户id列表
        List<Long> uidList = billMapper.selectByNoRecord(thisMonthMonday.getYear(),
                thisMonthMonday.getMonth().getValue(), null, BillTypeEnum.LIFE.msg);


        //第几页
        int page = 0;
        //查询几个
        int size = 100;

        // 将LocalDate转换为Date
        Date startDateTime = Date.from(thisMonthMonday.atStartOfDay(ZoneId.systemDefault()).toInstant());
        //获取所有的有效期内的合同
        LambdaQueryWrapper<Contractual> contractualWrapper = new LambdaQueryWrapper<>();
        contractualWrapper.eq(Contractual::getContractualState, ContractualStateEnum.VALID.code)
                .lt(Contractual::getStartTime, startDateTime).in(uidList.size() > 0, Contractual::getContractualUser, uidList);
        Page<Contractual> pageQuery = new Page<>(page, size);
        pageQuery.setSearchCount(false);
        do {
            page += 1;
            pageQuery.setCurrent(page);
            //每次查询100条用户记录
            Page<Contractual> pageList = contractualMapper.selectPage(pageQuery, contractualWrapper);
            if (pageList.getRecords().size() < 1 || uidList.size() < 1) {
                break;
            }

            //创建账单
            pageList.getRecords().forEach(s -> {

                // 获取上个月的最后一天
                LocalDate lastDayOfLastMonth = LocalDate.now().minusMonths(1)
                        .with(TemporalAdjusters.lastDayOfMonth());


                // 获取本月第一天
                LocalDate firstDayOfMonth = LocalDate.now().withDayOfMonth(2);

                //查询前两个月的使用的水电表记录
                LambdaQueryWrapper<ResourceUsage> resourceUsageWrapper = new LambdaQueryWrapper<>();
                resourceUsageWrapper.eq(ResourceUsage::getHouseId, s.getHouseId())
                        .le(ResourceUsage::getCreateTime, firstDayOfMonth)
                        .ge(ResourceUsage::getCreateTime, lastDayOfLastMonth)
                        .orderByDesc(ResourceUsage::getCreateTime);

                List<ResourceUsage> records = resourceUsageMapper.selectList(resourceUsageWrapper);

                if (records.size() == 2) {
                    //本月第一天以及上月最后一天的水电使用记录
                    ResourceUsage useFirst = records.get(0);
                    ResourceUsage useLast = records.get(1);
                    Bill bill = new Bill();
                    bill.setBillUser(s.getContractualUser());
                    bill.setBillCategory(BillTypeEnum.LIFE.msg);
                    bill.setBillState(BillStateEnum.WARN_PAY.code);
                    bill.setBillMsg(s.getHouseNumber());
                    Long watersFees = s.getWaterFess() * (useFirst.getWater() - useLast.getWater());
                    Long tariffFees = s.getTariffFees() * (useFirst.getElectricity() - useLast.getElectricity());
                    Long cngFees = s.getCngFees() * (useFirst.getCng() - useLast.getCng());
                    Long total_price = lifeBillCalculations(watersFees, tariffFees, cngFees);
                    bill.setBillPrice(total_price);
                    BillLife billLife = new BillLife(
                            watersFees,
                            tariffFees,cngFees,
                            s.getPropertyAddress());
                    bill.setBillSpecifics(JSON.toJSONString(billLife));
                    if (billMapper.insert(bill) < 1) {
                        throw new BusinessException(ResultEnum.ERROR_ADD_BILL);
                    }
                }
            });
        } while (true);

    }


    /**
     * 房屋每月账单计算
     *
     * @param houseRent    房屋租金
     * @param serviceFees  服务费
     * @param internetFees internetFees 网络费
     * @return 账单总额
     */
    Long housingBillCalculations(Long houseRent, Long serviceFees, Long internetFees) {
        BigDecimal houseRentPrice = BigDecimal.valueOf(houseRent);
        BigDecimal serviceFeesPrice = BigDecimal.valueOf(serviceFees);
        BigDecimal internetFeesPrice = BigDecimal.valueOf(internetFees);
        BigDecimal price = houseRentPrice.add(serviceFeesPrice).add(internetFeesPrice);
        if (price.compareTo(BigDecimal.valueOf(Long.MIN_VALUE)) < 0 ||
                price.compareTo(BigDecimal.valueOf(Long.MAX_VALUE)) > 0) {
            throw new BusinessException(ResultEnum.ERROR_TYPE_BILL);
        }
        return price.longValueExact();
    }


    /**
     * 房屋每月水、电、天然气费计算
     *
     * @param water       水费
     * @param electricity 电费
     * @param cng         天然气费
     * @return 账单总额
     */
    Long lifeBillCalculations(Long water, Long electricity, Long cng) {
        BigDecimal waterFees = BigDecimal.valueOf(water);
        BigDecimal electricityFees = BigDecimal.valueOf(electricity);
        BigDecimal cngFees = BigDecimal.valueOf(cng);
        BigDecimal price = waterFees.add(electricityFees).add(cngFees);
        if (price.compareTo(BigDecimal.valueOf(Long.MIN_VALUE)) < 0 ||
                price.compareTo(BigDecimal.valueOf(Long.MAX_VALUE)) > 0) {
            throw new BusinessException(ResultEnum.ERROR_BILL_PRICE);
        }
        return price.longValueExact();
    }
}

